﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace BlackStar
{
    public class ThemePartContentReader : ContentTypeReader<ThemePart>
    {
        protected override ThemePart Read(ContentReader input, ThemePart existingInstance)
        {
            ThemePart part = new ThemePart();
            part.ReadContent(input);
            return part;
        }
    }

    public class StretchThemePartContentReader : ContentTypeReader<StretchThemePart>
    {
        protected override StretchThemePart Read(ContentReader input, StretchThemePart existingInstance)
        {
            StretchThemePart part = new StretchThemePart();
            part.ReadContent(input);
            return part;
        }
    }

    public class ThemePart
    {
        internal GUI myGui;
        public string AssetName;
        public Point MinimumSize = Point.Zero;

        [ContentSerializerIgnore]
        protected Texture2D texture;

        public Dictionary<string, Rectangle> Rectangles = new Dictionary<string, Rectangle>();

        public virtual void Draw(string style, Rectangle dest, Color color)
        {
            if (Rectangles.ContainsKey(style))
            { myGui.batch.Draw(texture, dest, Rectangles[style], color); }
            else
            { myGui.batch.Draw(texture, dest, null, color); }
        }

        internal virtual void Load(ContentReader input)
        {
            string assetDir = input.AssetName.Substring(0, input.AssetName.LastIndexOf("\\") + 1);
            texture = input.ContentManager.Load<Texture2D>(assetDir + this.AssetName);
        }

        protected internal virtual void ReadContent(ContentReader input)
        {
            AssetName = input.ReadString();
            Rectangles = input.ReadObject<Dictionary<string, Rectangle>>();
            MinimumSize = input.ReadObject<Point>();

            this.Load(input);
        }

        [ContentSerializerIgnore]
        public GUI GUI
        {
            get { return myGui; }
            set { myGui = value; }
        }
    }

    public class StretchThemePart : ThemePart 
    {

        public Dictionary<string, List<string>> PartLists = new Dictionary<string, List<string>>();

        public override void Draw(string style, Rectangle dest, Color color)
        {
            Rectangle[] destRects = new Rectangle[9];
            List<string> parts;

            if (PartLists.ContainsKey(style))
            { parts = PartLists[style]; }
            else
            { parts = PartLists["Default"]; } 


            int X1, X2, X3, Y1, Y2, Y3;
            int W1, W2, W3, H1, H2, H3;

            W1 = Rectangles[parts[0]].Width;
            W3 = Rectangles[parts[2]].Width;
            W2 = dest.Width - (W1 + W3);

            H1 = Rectangles[parts[0]].Height;
            H3 = Rectangles[parts[6]].Height;
            H2 = dest.Height - (H1 + H3);

            X1 = dest.X;
            X2 = X1 + W1;
            X3 = X2 + W2;

            Y1 = dest.Y;
            Y2 = Y1 + H1;
            Y3 = Y2 + H2;

            destRects[0] = new Rectangle(X1, Y1, W1, H1);
            destRects[1] = new Rectangle(X2, Y1, W2, H1);
            destRects[2] = new Rectangle(X3, Y1, W3, H1);
            destRects[3] = new Rectangle(X1, Y2, W1, H2);
            destRects[4] = new Rectangle(X2, Y2, W2, H2);
            destRects[5] = new Rectangle(X3, Y2, W3, H2);
            destRects[6] = new Rectangle(X1, Y3, W1, H3);
            destRects[7] = new Rectangle(X2, Y3, W2, H3);
            destRects[8] = new Rectangle(X3, Y3, W3, H3);

            for (int i = 0; i < 9; i++)
            {
                myGui.batch.Draw(this.texture, destRects[i], Rectangles[parts[i]], color);
            }
        }

        protected internal override void ReadContent(ContentReader input)
        {
            base.ReadContent(input);

            PartLists = input.ReadObject<Dictionary<string, List<string>>>();
            // check that their is a default part list.
            if (!PartLists.ContainsKey("Default"))
            { throw new ApplicationException("A Default PartList must be specified."); }
            // check that the part lists do not contain refrences to 
            // rectangles that do not exist.
            CheckPartLists();
        }

        private void CheckPartLists()
        {
            foreach (KeyValuePair<string, List<string>> style in this.PartLists)
            {
                if (style.Value.Count != 9)
                {
                    throw new ApplicationException("The style '" + style.Key +
                        "' does not have 9 parts.");
                }

                foreach (string key in style.Value)
                {
                    if (!Rectangles.ContainsKey(key))
                    {
                        throw new ApplicationException("The style '" + style.Key +
                             "' uses a rectangle with the key '" + key +
                             "' which is undefined.");
                    }
                }
            }
        }
    }

    public class ThemeContentReader : ContentTypeReader<Theme>
    {
        protected override Theme Read(ContentReader input, Theme existingInstance)
        {

            Theme theme = new Theme();
            theme.ReadContent(input);
            return theme;
        }
    }

    public class Theme
    {
        internal GUI myGui;

        public CursorList Cursors = new CursorList();
        public FontList Fonts = new FontList();
        public StretchThemePart Form;
        public StretchThemePart Button;
        public StretchThemePart Textbox;
        public ThemePart Toggle;
        public ThemePart Scrollbar;

        [ContentSerializerIgnore]
        public Texture2D pixel;

        protected internal void ReadContent(ContentReader input)
        {
            Cursors = input.ReadObject<CursorList>();
            if (!Cursors.ContainsKey("Default")) throw new ApplicationException("A cursor named 'Default' must be defined.");

            Fonts = input.ReadObject<FontList>();
            Form = input.ReadObject<StretchThemePart>();
            Button = input.ReadObject<StretchThemePart>();
            Textbox = input.ReadObject<StretchThemePart>();
            Toggle = input.ReadObject<ThemePart>();
            Scrollbar = input.ReadObject<ThemePart>();
        }

        public Theme()
        {

        }

        public virtual void LoadContent()
        {
            // we add a event handler for the graphics device reset, because
            // the pixel texture will get lost, and we need to recreate it whenever a 
            // reset occurs.
            myGui.Game.GraphicsDevice.DeviceReset += new EventHandler(GraphicsDevice_DeviceReset);
            pixel = CreatePixelTexture(GUI.GraphicsDevice, 1, 1, Color.White);
        }

        void GraphicsDevice_DeviceReset(object sender, EventArgs e)
        {
            pixel = CreatePixelTexture(GUI.GraphicsDevice, 1, 1, Color.White);
        }

        protected Texture2D CreatePixelTexture(GraphicsDevice device, int width, int height, Color color)
        {
            RenderTarget2D LevelRenderTarget = new RenderTarget2D(device, width, height, 1,
                device.PresentationParameters.BackBufferFormat, device.PresentationParameters.MultiSampleType,
                device.PresentationParameters.MultiSampleQuality, RenderTargetUsage.PreserveContents);

            DepthStencilBuffer stencilBuffer = new DepthStencilBuffer(device, width, height,
                device.DepthStencilBuffer.Format, device.PresentationParameters.MultiSampleType,
                device.PresentationParameters.MultiSampleQuality);

            device.SetRenderTarget(0, LevelRenderTarget);

            // Cache the current depth buffer
            DepthStencilBuffer old = device.DepthStencilBuffer;
            // Set our custom depth buffer
            device.DepthStencilBuffer = stencilBuffer;

            device.Clear(color);

            device.SetRenderTarget(0, null);

            // Reset the depth buffer
            device.DepthStencilBuffer = old;
            return LevelRenderTarget.GetTexture();
        }

        [ContentSerializerIgnore]
        public GUI GUI
        {
            get { return myGui; }
            set
            {
                myGui = value;
                Fonts.GUI = value;
                Form.GUI = value;
                Button.GUI = value;
                Textbox.GUI = value;
                Toggle.GUI = value;
                Scrollbar.GUI = value;
            }
        }

        public virtual void Update(GameTime gameTime) { }
    }
}
